import org.junit.Test;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;

/**
 * 1.自定义泛型类/接口
 * 在声明完自定义泛型以后，可以在类的内部实用类的泛型；
 * 在创建自定义泛型类的对象时，可以指明泛型参数类型。一旦指明，内部凡是使用类的泛型参数的位置，都具体化为指定的类的泛型类型。
 * 如果在创建自定义泛型类的对象时，没有指明泛型参数类型，那么泛型将被擦除，泛型对应的类型均按照Object处理，但不等价于Object。
 * 除创建泛型对象外，子类继承泛型类时、实现类实现泛型接口时，也可以确定泛型结构中的泛型参数。
 * 注意：
 * ① 泛型类可能有多个参数，此时应将多个参数一起放在尖括号内。< E,T,P >
 * ② 如果泛型结构是一个接口或抽象类，则不可创建泛型类的对象。
 * ③ 不能使用 new T[] .但是可以：E[] elements = (E[]) new Object[capacity];
 * 参考：ArrayList源码中声明：Object[] elementData, 而非泛型参数类型数组。
 * ④ 在类、接口上声明的泛型，在本类或本接口中即代表某种类型，但不可以在静态方法中使用类的泛型。
 * ⑤ 异常类不能带泛型。
 * <p>
 * 2.自定义泛型方法
 * 声明泛型方法时，一定要添加泛型参数<T>
 * 泛型参数在方法调用时，指明其具体的类型
 * 泛型方法可以根据需要声明为static的
 * 泛型方法所属的类是否是一个泛型类，都可以。
 * <p>
 * 3.泛型在继承上的体现
 * --类SuperA是类A的父类：G<A>和G<SuperA>是并列的两个类，没有任何字符类关系。
 * --类SuperA是类A的父类或接口，SuperA<G> 与 A<G> 有继承或实现的关系。
 * <p>
 * 4.通配符 ?
 * 举例：ArrayList<?>
 * G<?>可以看作是G<A>类型的父类，即可以将G<A>的对象赋值给G<?>类型的引用或变量
 * 读写数据的特点：
 * > 读取数据：允许，读取的值的类型为Object类型
 * > 写入数据：不允许，特例：null
 */
public class GenericTest {
    // 测试自定义的泛型类
    @Test
    public void testOrder() {
        // 实例化时，就可以指明类的泛型参数的类型
        // 泛型参数在指明时，是不可以使用基本数据类型的！但是可以使用包装类替代基本数据类型。
        // 在实例化时，可以指明类的泛型参数的具体类型！
        // 一旦指明了泛型的类型，则在泛型类中凡是使用泛型参数的位置，都替换为指定的类型。
        Order<String> order = new Order<>();
        order.setT("Tom");
        System.out.println(order);
        SubOrder4<String> subOrder4 = new SubOrder4<>();

    }

    // 测试泛型方法
    @Test
    public void testMethod() {
        Order<String> order = new Order<>();
        Integer[] arr = new Integer[]{1, 2, 3, 4, 5};
        ArrayList<Integer> integerArrayList = order.copyFromArrayList(arr);
        System.out.println(integerArrayList);
    }

    @Test
    public void test1() {
        ArrayList<Objects> list1 = null;
        ArrayList<String> list2 = null;

//        list1 = list2;  // 报错

    }

    @Test
    public void test2() {
        //有限制条件的通配符的使用
        // ? extends A <=   可读不可写，null除外
        // ? super B   >=

        HashSet<Integer> hashSet = new HashSet<>();
        hashSet.add(1);
        hashSet.add(12);
        hashSet.add(11);
        for (Integer i :
                hashSet) {
            System.out.println(i);
        }

//        String s = "  ";
//        s.sub
    }
}
